import sys
from xml.sax import make_parser
from jd_schema import SchemaTable,SchemaColumn
from xdbdesigner import DBDesignerHandler,TypeDefine
from datetime import datetime

def do_template(strFormat, dictValue):
        return strFormat % dictValue

def do_optional_template(dictFormat, dictValue, key):
        if dictFormat.has_key(key) :
                return dictFormat[key] % dictValue
        return ''


###------------------------
###Template for declaration(.h)
###------------------------
TPL_H_FRAME="""/**
 * @file %(tableName)s.h
 * @author 
 * @date %(gen_date)s
 * @brief data-access declaration for table["%(tableName)s"]
 */
#ifndef _T_%(macroName)s_H_
#define _T_%(macroName)s_H_
#include "comm_data_access.h"
#include <string>
#include <vector>

using namespace WEB_GROUP;

/**
 * @struct ST%(entityName)s
 */
struct ST%(entityName)s
{
	%(loopstr_field)s
	
	ST%(entityName)s()
	{
		%(loopstr_init_field)s
	}
	
	void EscapeAll()
	{
		%(loopstr_escape_field)s
	}

	int SizeOfAllStr()
	{
	}
};

/**
 * @class C%(entityName)s
 */
class C%(entityName)s
{
public:
	static int Insert(ST%(entityName)s st%(entityName)s);
	static int Select(%(first_pk_param)s, const std::string& sCondition, int iStart, int iRow, std::vector<ST%(entityName)s> &out_vST%(entityName)s);
	static int Count(%(first_pk_param)s, const std::string& sCondition);
	static int SelectOne(%(loopstr_class_pk_param)s, ST%(entityName)s &out_ST%(entityName)s);
	static int Delete(%(loopstr_class_pk_param)s);
	static int Update(%(loopstr_class_pk_param)s, ST%(entityName)s st%(entityName)s);
	static int UpdatePartial(%(loopstr_class_pk_param)s, const string& sWrapped_Field_and_Value);
private:
	static const char* _GetTableName(%(first_pk_param)s);
	static int _Fill_SelectOne(CMysql *pMysql, void* p_st%(entityName)s);
	static int _Fill_Select(CMysql *pMysql, void* p_vst%(entityName)s);
};

#endif /*_T_%(macroName)s_H_*/

"""
TPL_STRUCT_FIELD='%(codeType)s %(codeField)s;'
TPL_STRUCT_INIT_FIELD_d={'int':'this->%(codeField)s = 0;',
                         'bool':'this->%(codeField)s = false;'}
TPL_STRUCT_ESCAPE_STR_d={'std::string':'CMysql::EscapeString( this->%(codeField)s );'}

TPL_FIELD_PARAM_d={'int':'int %(codeField)s',
                      'float':'float %(codeField)s',
                      'double':'double %(codeField)s',
                      'std::string':'const std::string &%(codeField)s'}

TPL_FIELD_VALUE_d={'int':'%(codeField)s',
                      'float':'%(codeField)s',
                      'double':'%(codeField)s',
                      'std::string':'%(codeField)s.c_str()'}


###---------------------------------
###Template for class implementation(.cpp)
###---------------------------------
TPL_CPP_FRAME="""/**
 * @file %(tableName)s.cpp
 * @author 
 * @date %(gen_date)s
 * @brief data-access implementation for table["%(tableName)s"]
 */
#include "%(tableName)s.h"
#include "api.h"
#include "define.h"

#define TPL_SQL_%(macroName)s_INSERT "INSERT INTO %%s SET %(loopstr_sql_insert_field)s "

#define TPL_SQL_%(macroName)s_SELECT "SELECT %(loopstr_sql_select_timestamp)s FROM %%s WHERE %%s LIMIT %%d,%%d "

#define TPL_SQL_%(macroName)s_COUNT "SELECT COUNT(*) AS TOTAL_COUNT FROM %%s WHERE %%s "

#define TPL_SQL_%(macroName)s_SELECTONE "SELECT %(loopstr_sql_select_timestamp)s FROM %%s WHERE %(loopstr_sql_pk_condition)s "

#define TPL_SQL_%(macroName)s_DELETE "DELETE FROM %%s WHERE %(loopstr_sql_pk_condition)s "

#define TPL_SQL_%(macroName)s_UPDATEPART "UPDATE %%s SET %%s WHERE %(loopstr_sql_pk_condition)s "

#define TPL_SQL_%(macroName)s_UPDATE "UPDATE %%s SET %(loopstr_sql_update_field)s WHERE %(loopstr_sql_pk_condition)s "

int C%(entityName)s::Insert(ST%(entityName)s st%(entityName)s)
{
	ST%(entityName)s st = st%(entityName)s;
	st.EscapeAll();
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_INSERT, _GetTableName(st.%(first_pk_codeField)s)
		,%(loopstr_class_insert_field)s);
	return _Commit_Simple_Query(sSQL.c_str());
}

int C%(entityName)s::Select(%(first_pk_param)s, const std::string& sCondition, int iStart, int iRow, std::vector<ST%(entityName)s> &out_vST%(entityName)s)
{
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_SELECT, _GetTableName(%(first_pk_codeField)s)
		,sCondition.c_str()
		,iStart
		,iRow);
	return _Commit_Select_Query(sSQL.c_str(), _Fill_Select, &out_vST%(entityName)s);
}

int C%(entityName)s::Count(%(first_pk_param)s, const std::string& sCondition)
{
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_COUNT, _GetTableName(%(first_pk_codeField)s)
		,sCondition.c_str());
	return _Commit_GetCount_Query(sSQL.c_str(), "TOTAL_COUNT");
}

int C%(entityName)s::SelectOne(%(loopstr_class_pk_param)s, ST%(entityName)s &out_ST%(entityName)s)
{
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_SELECTONE, _GetTableName(%(first_pk_codeField)s)
		,%(loopstr_class_pk_field)s);
	return _Commit_Select_Query(sSQL.c_str(), _Fill_SelectOne, &out_ST%(entityName)s);
}

int C%(entityName)s::Delete(%(loopstr_class_pk_param)s)
{
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_DELETE, _GetTableName(%(first_pk_codeField)s)
		,%(loopstr_class_pk_field)s);
	return _Commit_Simple_Query(sSQL.c_str());
}

int C%(entityName)s::Update(%(loopstr_class_pk_param)s, ST%(entityName)s st%(entityName)s)
{
	ST%(entityName)s st = st%(entityName)s;
	st.EscapeAll();
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_UPDATE, _GetTableName(%(first_pk_codeField)s)
		,%(loopstr_class_update_field)s
		,%(loopstr_class_pk_field)s);
	return _Commit_Simple_Query(sSQL.c_str());
}

int C%(entityName)s::UpdatePartial(%(loopstr_class_pk_param)s, const string& sWrapped_Field_and_Value)
{
	string sSQL("");
	CAPI::API_Sprintf(sSQL, TPL_SQL_%(macroName)s_UPDATEPART, _GetTableName(%(first_pk_codeField)s)
		,sWrapped_Field_and_Value.c_str()
		,%(loopstr_class_pk_field)s);
	return _Commit_Simple_Query(sSQL.c_str());
}

const char* C%(entityName)s::_GetTableName(%(first_pk_param)s)
{
	//TODO: do it yourself
	assert(false);
	static char szTmp[256];
	memset(szTmp,0x00,sizeof(szTmp));
	sprintf(szTmp,"%%s","%(tableName)s");
	return szTmp;
}

#define SAFE_STR(STR) (NULL==STR)?"":STR

int C%(entityName)s::_Fill_Select(CMysql *pMysql, void* p_vst%(entityName)s)
{
	vector<ST%(entityName)s>* v = (vector<ST%(entityName)s>*)p_vst%(entityName)s;
	v->clear();
	while(pMysql->FetchRow())
	{
		ST%(entityName)s st;
		%(loopstr_class_select_set_field)s
		v->push_back(st);
	}
	return v->size();
}

int C%(entityName)s::_Fill_SelectOne(CMysql *pMysql, void* p_st%(entityName)s)
{
	ST%(entityName)s* st = (ST%(entityName)s*)p_st%(entityName)s;
	if(pMysql->FetchRow())
	{
		%(loopstr_class_selectone_set_field)s
		return 1;
	}
	return 0;
}


#undef SAFE_STR
#undef TPL_SQL_%(macroName)s_INSERT
#undef TPL_SQL_%(macroName)s_SELECT
#undef TPL_SQL_%(macroName)s_COUNT
#undef TPL_SQL_%(macroName)s_SELECTONE
#undef TPL_SQL_%(macroName)s_DELETE
#undef TPL_SQL_%(macroName)s_UPDATE
#undef TPL_SQL_%(macroName)s_UPDATEPART



"""

TPL_SQL_INSERT_UPDATE_FIELD_d={'int':"%(field)s = %%d",
                        'std::string':"%(field)s = '%%s'",
                        'float':"%(field)s = %%f",
                        'double':"%(field)s = %%f",
                        'bool':"%(field)s = %%d",
                        'TIMESTAMP':"%(field)s = UNIX_TIMESTAMP('%%s')"}

TPL_SQL_SELECT_TIMESTAMP='FROM_UNIXTIME(%(field)s) AS %(field)s_txt'
TPL_SQL_PK_CONDITION_d={'int':"%(field)s = %%d",
                        'std::string':"%(field)s = '%%s'",
                        'float':"%(field)s = %%f",
                        'double':"%(field)s = %%f",
                        'bool':"%(field)s = %%d"}



TPL_INSERT_UPDATE_SET_VALUE_d={'int':'st.%(codeField)s',
                      'float':'st.%(codeField)s',
                      'double':'st.%(codeField)s',
                      'std::string':'st.%(codeField)s.c_str()'}

TPL_SELECT_SET_VALUE_d={'int':'st.%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'std::string':'st.%(codeField)s = SAFE_STR(pMysql->GetField( "%(field)s" ));',
                        'float':'st.%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'double':'st.%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'bool':'st.%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'TIMESTAMP':'st.%(codeField)s = SAFE_STR(pMysql->GetField( "%(field)s_txt" ));'}

TPL_SELECTONE_SET_VALUE_d={'int':'st->%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'std::string':'st->%(codeField)s = SAFE_STR(pMysql->GetField( "%(field)s" ));',
                        'float':'st->%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'double':'st->%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'bool':'st->%(codeField)s = atoi(SAFE_STR(pMysql->GetField( "%(field)s" )));',
                        'TIMESTAMP':'st->%(codeField)s = SAFE_STR(pMysql->GetField( "%(field)s_txt" ));'}

if __name__ == '__main__':
	if(len(sys.argv)<2):
		print "usage: %s input_file [output_dir]" % sys.argv[0]
		exit()
	output_dir = './output'
	if len(sys.argv)>=3 :
                output_dir = sys.argv[2]
	hdl = DBDesignerHandler()
	parser = make_parser()
	parser.setContentHandler(hdl)
	parser.parse(sys.argv[1])
	
	print '%d (s) is found.' % len(hdl.Tbs)
	schemaTables = hdl.getSchemaTables()    
	#test
	for tb in schemaTables:
                print 'generating tables["%s"]' % tb.tableName
                if len(tb.pks)<1:
                        print '\t\t NO Primary Key, Ignoring this table[%s].' % tb.tableName
                        continue
		sOutPathH='%s/%s.h'%(output_dir,tb.tableName)
		sOutPathCPP='%s/%s.cpp'%(output_dir,tb.tableName)
		f_h = open(sOutPathH,'w')
		f_cpp = open(sOutPathCPP,'w')
		#sql
		str_sql_insert_fields = []
		str_sql_update_fields = []
		str_sql_select_timestamps = ['*']
		str_str_sql_pk_conditions = []
                #struct
		str_fields = []
		str_init_fields = []
		str_escape_fields = []
		#class
		str_str_class_pk_params = []
		str_class_pk_fields = []
		str_class_insert_fields = []
		str_class_update_fields = []
		str_class_select_set_fields = []
		str_class_selectone_set_fields = []
		for col in tb.columns:
			#========SQL
			if 'std::string'==col.codeType and col.field.lower().find('time')>=0:
        			tmp = do_template(TPL_SQL_SELECT_TIMESTAMP, col.getDict())
                                str_sql_select_timestamps.append(tmp)
                                tmp = do_optional_template( TPL_SQL_INSERT_UPDATE_FIELD_d,  col.getDict(), "TIMESTAMP")
                                if len(tmp)>0 : str_sql_insert_fields.append(tmp)
                                if not col.isPK :
                                        tmp = do_optional_template( TPL_SQL_INSERT_UPDATE_FIELD_d,  col.getDict(), "TIMESTAMP")
                                        if len(tmp)>0 : str_sql_update_fields.append(tmp)
                        else:
                                tmp = do_optional_template( TPL_SQL_INSERT_UPDATE_FIELD_d,  col.getDict(), col.codeType)
                                if len(tmp)>0 : str_sql_insert_fields.append(tmp)
                                if not col.isPK :
                                        tmp = do_optional_template( TPL_SQL_INSERT_UPDATE_FIELD_d,  col.getDict(), col.codeType)
                                        if len(tmp)>0 : str_sql_update_fields.append(tmp)
                                
                        #========struct
			tmp = do_template(TPL_STRUCT_FIELD, col.getDict())
			str_fields.append(tmp)
			tmp = do_optional_template( TPL_STRUCT_INIT_FIELD_d,  col.getDict(), col.codeType)
			if len(tmp)>0 : str_init_fields.append(tmp)
                        tmp = do_optional_template( TPL_STRUCT_ESCAPE_STR_d,  col.getDict(), col.codeType)
			if len(tmp)>0 : str_escape_fields.append(tmp)
			#========class
                        tmp = do_optional_template( TPL_INSERT_UPDATE_SET_VALUE_d,  col.getDict(), col.codeType)
			if len(tmp)>0 : str_class_insert_fields.append(tmp)
			if not col.isPK :
                                tmp = do_optional_template( TPL_INSERT_UPDATE_SET_VALUE_d,  col.getDict(), col.codeType)
                                if len(tmp)>0 : str_class_update_fields.append(tmp)
			if 'std::string'==col.codeType and col.field.lower().find('time')>=0:
                                tmp = do_optional_template( TPL_SELECT_SET_VALUE_d,  col.getDict(), 'TIMESTAMP')
                                if len(tmp)>0 : str_class_select_set_fields.append(tmp)
                                tmp = do_optional_template( TPL_SELECTONE_SET_VALUE_d,  col.getDict(), 'TIMESTAMP')
                                if len(tmp)>0 : str_class_selectone_set_fields.append(tmp)
			else:
                                tmp = do_optional_template( TPL_SELECT_SET_VALUE_d,  col.getDict(), col.codeType)
                                if len(tmp)>0 : str_class_select_set_fields.append(tmp)
                                tmp = do_optional_template( TPL_SELECTONE_SET_VALUE_d,  col.getDict(), col.codeType)
                                if len(tmp)>0 : str_class_selectone_set_fields.append(tmp)
			
                for col in tb.pks:
			#========SQL
			tmp = do_optional_template( TPL_SQL_PK_CONDITION_d,  col.getDict(), col.codeType)
                        if len(tmp)>0 : str_str_sql_pk_conditions.append(tmp)
			#========class
			tmp = do_optional_template( TPL_FIELD_PARAM_d,  col.getDict(), col.codeType)
                        if len(tmp)>0 : str_str_class_pk_params.append(tmp)
			tmp = do_optional_template( TPL_FIELD_VALUE_d,  col.getDict(), col.codeType)
                        if len(tmp)>0 : str_class_pk_fields.append(tmp)
                        
		f_h.write(TPL_H_FRAME%{'gen_date':datetime.now().strftime('%Y-%m-%d'),
                                       'macroName':tb.macroName,
                                       'entityName':tb.entityName,
                                       'tableName':tb.tableName,
                                       'loopstr_field':'\n\t'.join(str_fields),
                                       'loopstr_init_field':'\n\t\t'.join(str_init_fields),
                                       'loopstr_escape_field':'\n\t\t'.join(str_escape_fields),
                                       'loopstr_class_pk_param':', '.join(str_str_class_pk_params),
                                       'first_pk_param': do_optional_template(TPL_FIELD_PARAM_d,tb.pks[0].getDict(),tb.pks[0].codeType)})
		f_h.close()
		
		f_cpp.write(TPL_CPP_FRAME%{'gen_date':datetime.now().strftime('%Y-%m-%d'),
                                           'macroName':tb.macroName,
                                           'entityName':tb.entityName,
                                           'tableName':tb.tableName,
                                           'loopstr_sql_insert_field':', '.join(str_sql_insert_fields),
                                           'loopstr_sql_update_field':', '.join(str_sql_update_fields),
                                           'loopstr_sql_select_timestamp':', '.join(str_sql_select_timestamps),
                                           'loopstr_sql_pk_condition':' and '.join(str_str_sql_pk_conditions),
                                           'loopstr_class_pk_param':', '.join(str_str_class_pk_params),
                                           'loopstr_class_pk_field':'\n\t\t,'.join(str_class_pk_fields),
                                           'first_pk_codeField': do_optional_template(TPL_FIELD_VALUE_d, tb.pks[0].getDict(),tb.pks[0].codeType),
                                           'first_pk_param': do_optional_template(TPL_FIELD_PARAM_d,tb.pks[0].getDict(),tb.pks[0].codeType),
                                           'loopstr_class_insert_field':'\n\t\t,'.join(str_class_insert_fields),
                                           'loopstr_class_update_field':'\n\t\t,'.join(str_class_update_fields),
                                           'loopstr_class_select_set_field':'\n\t\t'.join(str_class_select_set_fields),
                                           'loopstr_class_selectone_set_field':'\n\t\t'.join(str_class_selectone_set_fields),
                                           })
		f_cpp.close()

